﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AppLimit.CloudComputing.SharpBox;
using AppLimit.CloudComputing.SharpBox.StorageProvider.DropBox;
using System.IO;
using AppLimit.CloudComputing.SharpBox.Exceptions;
namespace CipherBox
{
    public enum FileCompare { Same, RemoteIsNewer, LocalIsNewer, Obstructed };
    public class DropBox
    {
        #region Fields
        protected Dictionary<char, string> unallowedChars = new Dictionary<char, string>() { { '\\', "(81)" }, { '/', "(72)" }, { ':', "(63)" }, { '*', "(54)" }, { '<', "(45)" }, { '>', "(36)" }, { '"', "(27)" }, { '|', "(18)" } };
        protected string m_EncryptionKey;
        protected FileOperationProgressChanged m_ProgressChanged;
        protected DropBoxConfiguration m_Config;
        protected ICloudStorageAccessToken m_Token;
        protected CloudStorage m_Storage;
        protected ICloudDirectoryEntry m_CurrentDir;
        protected List<string> m_currentPath = new List<string>();
        protected string m_RootDirectoryString;
        protected Dictionary<string, List<ICloudFileSystemEntry>> m_AllFiles = new Dictionary<string, List<ICloudFileSystemEntry>>();
        protected ICloudDirectoryEntry m_RootDirectory;
        #endregion Fields
        #region Properties
        public ICloudStorageAccessToken Token { get { return m_Token; } }

        public string[] AllDirectories
        {
            get
            {
                m_AllFiles = new Dictionary<string, List<ICloudFileSystemEntry>>();
                getAllDirectoriesAndFiles();
                var i = from entry in m_AllFiles.Keys
                        select decrypPath(entry).Replace(m_RootDirectoryString, "");
                return i.ToArray<string>();
            }
        }
        public string[] AllFiles
        {
            get
            {
                getAllDirectoriesAndFiles();
                List<string> files = new List<string>();
                foreach (string key in m_AllFiles.Keys)
                {
                    foreach (ICloudFileSystemEntry file in m_AllFiles[key])
                    {
                        RemotePath remotePath = new RemotePath(key + file.Name, m_RootDirectoryString);
                        files.Add(decrypPath(remotePath.RelativeFile));
                    }
                }
                return files.ToArray<string>();
            }
        }
        public string CurrentDirectory { get { return string.Join("/", m_currentPath); } }
        public string RootDirectory { get { return m_RootDirectoryString; } }
        #endregion Properties
        #region Constructor
        public DropBox(string username, string password, string encryptionKey, string rootDirectory)
        {
            m_Storage = new CloudStorage();
            m_Config = DropBoxConfiguration.GetStandardConfiguration();
            m_Config = new DropBoxConfiguration();
            DropBoxCredentials m_Credentials = new DropBoxCredentials();
            m_Credentials.ConsumerKey = "ax9kcmzsya1n355";
            m_Credentials.ConsumerSecret = "586il9r36yu5pbv";
            m_Credentials.UserName = username;
            m_Credentials.Password = password;
            m_Token = m_Storage.Open(m_Config, m_Credentials);
            if (!rootDirectory.StartsWith("/"))
                rootDirectory = "/" + rootDirectory;
            if (m_Storage.GetFolder(rootDirectory)==null)
                m_Storage.CreateFolder(rootDirectory);
            m_RootDirectoryString = rootDirectory;
            m_RootDirectory = m_Storage.GetFolder(rootDirectory);
            m_CurrentDir = m_RootDirectory;
            m_EncryptionKey = encryptionKey;
        }
        public DropBox(string username, string password) : this(username, password, string.Empty, "/") { }
        public DropBox(string tokenPath, string encryptionKey, string rootDirectory, FileOperationProgressChanged callback)
        {

            m_ProgressChanged = callback;
            m_Storage = new CloudStorage();
            FileStream fs = File.OpenRead(tokenPath);
            m_Token = m_Storage.DeserializeSecurityToken(fs);
            fs.Close();
            m_Config = DropBoxConfiguration.GetStandardConfiguration();
            m_Token = m_Storage.Open(m_Config, m_Token);
            if (!rootDirectory.StartsWith("/"))
                rootDirectory = "/" + rootDirectory;

            if (m_Storage.GetFolder(rootDirectory) == null)
                m_Storage.CreateFolder(rootDirectory);
            m_RootDirectoryString = rootDirectory;
            m_RootDirectory = m_Storage.GetFolder(rootDirectory);
            m_CurrentDir = m_RootDirectory;
            int z = m_RootDirectory.GetHashCode();
            m_EncryptionKey = encryptionKey;
        }
        public string[] GetChildDirectories()
        {
            var r = from entry in m_CurrentDir
                    where entry is ICloudDirectoryEntry
                    select entry.Name;
            return r.ToArray<string>();
        }
        #endregion Constructor
        #region Private Methods

        private void getAllDirectoriesAndFiles()
        {
            m_AllFiles = new Dictionary<string, List<ICloudFileSystemEntry>>();
            getAllDirectoriesAndFiles(m_RootDirectory, m_RootDirectoryString.Substring(0, m_RootDirectoryString.LastIndexOf("/")));

        }
        private void getAllDirectoriesAndFiles(ICloudDirectoryEntry path, string parent)
        {
            parent = parent + "/" + path.Name;
            var directories = from entry in path
                              where entry is ICloudDirectoryEntry
                              select entry;

            var files = from entry in path
                        where !(entry is ICloudDirectoryEntry)
                        select entry;
            RemotePath rp = new RemotePath(parent + "/", m_RootDirectoryString);
            if (!m_AllFiles.ContainsKey(rp.RelativeDirectory))
            {
                m_AllFiles[rp.RelativeDirectory] = new List<ICloudFileSystemEntry>();
            }
            m_AllFiles[rp.RelativeDirectory].AddRange(files);
            foreach (ICloudDirectoryEntry e in directories)
            {
                getAllDirectoriesAndFiles(e, parent);
            }
        }
        private string escape(string s)
        {
            foreach (char key in unallowedChars.Keys)
            {
                s = s.Replace(key.ToString(), unallowedChars[key]);
            }
            return s;
        }
        private string unescape(string s)
        {
            foreach (char key in unallowedChars.Keys)
            {
                s = s.Replace(unallowedChars[key], key.ToString());
            }
            return s;
        }
        #endregion Private Methods
        public void BrowseParent()
        {
            m_CurrentDir = (ICloudDirectoryEntry)m_CurrentDir.Parent;
            m_currentPath.RemoveAt(m_currentPath.Count - 1);
        }
        public void Browse(string name)
        {
            m_CurrentDir = (ICloudDirectoryEntry)m_CurrentDir.GetChild(name);
            m_currentPath.Add(name);
        }
        public bool DirectoryExists(string path)
        {
            path = encrypPath(path);
            try
            {
                if (m_Storage.GetFolder(path) == null)
                {
                    return false;
                }
                return true;
            }
            catch
            {
                return false;
            }
        }
        public DateTime GetTimestamp(string path)
        {
            path = encrypPath(path);
            RemotePath remotePath = new RemotePath(path, m_RootDirectoryString);
            string remoteFolder = remotePath.FullDirectory.Substring(0, remotePath.FullDirectory.Length - 1);
            if (path.EndsWith("/"))
            {
                return m_Storage.GetFolder(remoteFolder).Modified;
            }
            else
            {
                return m_Storage.GetFile(remotePath.File, m_Storage.GetFolder(remoteFolder)).Modified;
            }
        }
        public bool FileExists(string path)
        {
            path = encrypPath(path);
            try
            {
                RemotePath remotePath = new RemotePath(path, m_RootDirectoryString);
                ICloudFileSystemEntry dir = m_Storage.GetFile(remotePath.File, m_Storage.GetFolder(remotePath.FullDirectory));
                return dir != null;
            }
            catch (SharpBoxException)
            {
                return false;
            }
        }

        public FileCompare CompareFiles(string localPath, string remotePath, XMLDirInfo.directoryinfoSnyncfile syncFile)
        {
            remotePath = encrypPath(remotePath);
            if (syncFile == null)
            {
                return FileCompare.RemoteIsNewer;
            }
            RemotePath remotePathR = new RemotePath(remotePath, m_RootDirectoryString);

            FileInfo localFile = new FileInfo(localPath);

            ICloudDirectoryEntry dir = m_Storage.GetFolder(remotePathR.FullDirectory.Substring(0, remotePathR.FullDirectory.Length - 1));

            ICloudFileSystemEntry remoteFile = m_Storage.GetFile(remotePathR.File, dir);


            if (syncFile.local.file.timestamp == localFile.LastWriteTime)
            {
                if (remoteFile.Modified == syncFile.remote.file.timestamp)
                {
                    return FileCompare.Same;
                }
                else
                {
                    return FileCompare.RemoteIsNewer;
                }
            }
            else
            {
                if (remoteFile.Modified == syncFile.remote.file.timestamp)
                {
                    return FileCompare.LocalIsNewer;
                }
            }
            return FileCompare.Obstructed;
        }
        public void Upload(string localPath, string remotePath)
        {

            RemotePath path = new RemotePath(remotePath, m_RootDirectoryString);
            if (!DirectoryExists(path.FullDirectory.Substring(0, path.FullDirectory.Length - 1)))
                CreateDirectory(path.FullDirectory.Substring(0, path.FullDirectory.Length - 1));
            remotePath = encrypPath(remotePath);
            path = new RemotePath(remotePath, m_RootDirectoryString);
            string encryptedLocalPath = Path.GetDirectoryName(localPath) + "\\" + encrypFilename(Path.GetFileName(localPath));
            Crypt.Encrypt(localPath, encryptedLocalPath, m_EncryptionKey);
            m_Storage.UploadFile(encryptedLocalPath, path.FullDirectory.Substring(0, path.FullDirectory.Length - 1), m_ProgressChanged);
            File.Delete(encryptedLocalPath);
        }
        public void Download(string localPath, string remotePath)
        {
            remotePath = encrypPath(remotePath);
            RemotePath path = new RemotePath(remotePath, m_RootDirectoryString);
            string directory = Path.GetDirectoryName(localPath);
            if (!Directory.Exists(directory))
            {
                Directory.CreateDirectory(directory);
            }
            m_Storage.DownloadFile(path.FullPath, directory, m_ProgressChanged);
            Crypt.Decrypt(directory + "\\" + path.File, directory + "\\" + decrypFilename(path.File), m_EncryptionKey);
            File.Delete(directory + "\\" + path.File);
        }
        public void CreateDirectory(string path)
        {
            path = encrypPath(path);
            if (!path.EndsWith("/")) path += "/";
            RemotePath remotePath = new RemotePath(path, m_RootDirectoryString);
            m_Storage.CreateFolder(remotePath.FullDirectory.Substring(0, remotePath.FullDirectory.Length - 1));
        }
        public void DeleteFile(string file)
        {
            file = encrypPath(file);
            RemotePath remotePath = new RemotePath(file, m_RootDirectoryString);
            m_Storage.DeleteFileSystemEntry(remotePath.FullPath);
        }
        public void DeleteDirectory(string path)
        {
            path = encrypPath(path);
            RemotePath remotePath = new RemotePath(path, m_RootDirectoryString);
            m_Storage.DeleteFileSystemEntry(remotePath.FullDirectory.Substring(0, remotePath.FullDirectory.Length - 1));
        }
        public void SaveToken(string path)
        {
            Stream tokenstream = m_Storage.SerializeSecurityToken(m_Token);
            Stream tokenfile = new FileStream(path, FileMode.Create);
            tokenstream.CopyTo(tokenfile);
            tokenfile.Close();
        }

        protected string encrypPath(string path)
        {
            bool trailingSlash = path.EndsWith("/");
            path = path.Replace(m_RootDirectoryString, "");
            string[] parts = path.Split("/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            for (int i = 0; i < parts.Length; i++)
            {
                parts[i] = escape(encrypFilename(parts[i]));
            }
            return (m_RootDirectoryString + "/" + string.Join("/", parts) + ((trailingSlash) ? "/" : string.Empty)).Replace("//", "/");
        }
        protected string encrypFilename(string name)
        {
            return escape(Crypt.Encrypt(name, m_EncryptionKey));
        }
        protected string decrypFilename(string name)
        {
            return Crypt.Decrypt(unescape(name), m_EncryptionKey);
        }
        protected string decrypPath(string path)
        {
            bool trailingSlash = path.EndsWith("/");
            path = path.Replace(m_RootDirectoryString, "");
            string[] parts = path.Split("/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            for (int i = 0; i < parts.Length; i++)
            {
                parts[i] = decrypFilename(parts[i]);
            }
            return (m_RootDirectoryString + "/" + string.Join("/", parts) + ((trailingSlash) ? "/" : string.Empty)).Replace("//", "/");
        }

        public void Close()
        {
            m_Storage.Close();
        }




    }
}
